#ifndef _Ape__Collections_Array
#define _Ape__Collections_Array

#include <Ape/Object.h>
#include <Ape/Collections/CollectionHelpers.h>
#include <Ape/IntBase.h>

namespace Ape {
	namespace Collections {
	
		class Array_Impl : public Object {
			TYPE_NAME ("Ape::Collections::Array_Impl")
		private:
			void** m_Array;
			size_t m_Size;
			size_t m_Allocated;
		public:
			Array_Impl (size_t initialSize);
			virtual ~Array_Impl();
			
			size_t get_Size() const;
			size_t get_Allocated() const;
		protected:
			virtual void DisposePtr (void*) = 0;
			void Dispose();
			
			void Allocate (size_t length);
			
			void* get (size_t iPos) const;
			void set (size_t iPos, void* value);
		};
		
		template <typename E_t>
		class ArrayIterator;
		
		template <typename E_t>
		class Array : public Array_Impl {
			TYPE_NAME ("Ape::Collections::Array<E_t>")
		public:
			typedef ArrayIterator<E_t> Iterator_t;
			typedef Array<E_t> Container_t;
			
			Array (size_t initialSize, const E_t& defaultValue);
			virtual ~Array();
			
			const E_t operator[] (size_t iPos) const;
			E_t& operator[] (size_t iPos);
			
			const E_t GetAt (size_t iPos) const;
			void SetAt (size_t iPos, const E_t& val);
			
		protected:
			virtual void DisposePtr (void* p);
		};
		
// ----------------------------------------- //

		template <typename E_t>
		class ArrayIterator : public Object {
			TYPE_NAME ("Ape::Collections::ArrayIterator<E_t>")
		private:
			const Array<E_t>& m_Array;
			size_t m_Position;
			size_t m_ArraySize;
		public:
			ArrayIterator (const Array<E_t>& array);
			ArrayIterator (const ArrayIterator<E_t>& other);
			virtual ~ArrayIterator();
			
			operator const E_t () const;
			
			ArrayIterator<E_t>& operator ++ ();
			ArrayIterator<E_t> operator ++ (int /*unused*/);
			
			bool Continue() const;
			size_t get_Position() const;
			size_t get_Size() const;
			
			virtual const String ToString() const;
		};
		
// ----------------------------------------- //

		template <typename E_t>
		ArrayIterator<E_t>::ArrayIterator (const Array<E_t>& array)
				: m_Array (array),
				m_Position (0),
				m_ArraySize (array.get_Size() ) {}
				
		template <typename E_t>
		ArrayIterator<E_t>::~ArrayIterator() {}
		
		template <typename E_t>
		ArrayIterator<E_t>::ArrayIterator (const ArrayIterator<E_t>& other)
				: Object (other),
				m_Array (other.m_Array),
				m_Position (other.m_Position),
				m_ArraySize (other.m_ArraySize) {}
				
		template <typename E_t>
		ArrayIterator<E_t>::operator const E_t () const {
			return m_Array[m_Position];
		}
		
		template <typename E_t>
		ArrayIterator<E_t>& ArrayIterator<E_t>::operator ++ () {
			m_Position++;
			return *this;
		}
		
		template <typename E_t>
		ArrayIterator<E_t> ArrayIterator<E_t>::operator ++ (int /*unused*/) {
			ArrayIterator<E_t> copy (*this);
			m_Position++;
			return copy;
		}
		
		template <typename E_t>
		bool ArrayIterator<E_t>::Continue() const {
			return m_Position < m_ArraySize;
		}
		template <typename E_t>
		size_t ArrayIterator<E_t>::get_Position() const {
			return m_Position;
		}
		
		template <typename E_t>
		size_t ArrayIterator<E_t>::get_Size() const {
			return m_ArraySize;
		}
		
		template <typename E_t>
		const String ArrayIterator<E_t>::ToString() const {
			IntBase<size_t> p = m_Position;
			IntBase<size_t> s = m_ArraySize;
			return Object::ToString() + L" [" + p.ToString() + L"/" + s.ToString() + L"]";
		}
		
// ----------------------------------------- //

		template <typename E_t>
		Array<E_t>::Array (size_t initialSize, const E_t& defaultValue)
				: Array_Impl (initialSize)
		{
			for (size_t i = 0; i < initialSize; i++) {
				SetAt (i, defaultValue);
			}
		}
		
		template <typename E_t>
		Array<E_t>::~Array() {
			Dispose();
		}
		
		template <typename E_t>
		const E_t Array<E_t>::GetAt (size_t iPos) const {
			return TypedHelper<E_t>::ToStack (get (iPos) );
		}
		template <typename E_t>
		void Array<E_t>::SetAt (size_t iPos, const E_t& val) {
			void* v = TypedHelper<E_t>::ToHeap (val);
			try {
				set (iPos, v);
			}
			catch (...) {
				TypedHelper<E_t>::Free (v);
				throw;
			}
		}
		
		template <typename E_t>
		void Array<E_t>::DisposePtr (void* p) {
			TypedHelper<E_t>::Free (p);
		}
		
		template <typename E_t>
		const E_t Array<E_t>::operator[] (size_t iPos) const {
			return GetAt (iPos);
		}
		
		template <typename E_t>
		E_t& Array<E_t>::operator[] (size_t iPos) {
			E_t& ref = TypedHelper<E_t>::RefToStack (get (iPos) );
			return ref;
		}
		
	}
}

#endif // _Ape__Collections_Array
